////////////////////////////////////////////////////////////////////////////
//
//  Crytek Engine Source File.
//  Copyright (C), Crytek Studios, 2010.
// -------------------------------------------------------------------------
//  File name:   environemenprobeobject.h
//  Version:     v1.00
//  Created:     30/3/2010 by Johnmichael.
//  Compilers:   Visual Studio.NET
//  Description: 
// -------------------------------------------------------------------------
//  History:
//
////////////////////////////////////////////////////////////////////////////

#include "StdAfx.h"
#include "EnvironmentProbeObject.h"
#include "../EnvironmentProbePanel.h"
#include "Util/CubemapUtils.h"
#include "GameEngine.h"
#include "../Cry3DEngine/StatObj.h"
#include "../Cry3DEngine/Material.h"
#include "../Cry3DEngine/MatMan.h"
#include "../CryCommon/IShader.h"

//////////////////////////////////////////////////////////////////////////
// CBase implementation.
//////////////////////////////////////////////////////////////////////////
IMPLEMENT_DYNCREATE(CEnvironementProbeObject,CEntityObject)

//////////////////////////////////////////////////////////////////////////
// statics 
//////////////////////////////////////////////////////////////////////////
int CEnvironementProbeObject::s_panelProbeID = 0;
CEnvironmentProbePanel * CEnvironementProbeObject::s_pProbePanel = NULL;

//////////////////////////////////////////////////////////////////////////
CEnvironementProbeObject::CEnvironementProbeObject()
{
	m_entityClass = "EnvironmentLight";
	UseMaterialLayersMask(false);
}

//////////////////////////////////////////////////////////////////////////
bool CEnvironementProbeObject::Init( IEditor *ie,CBaseObject *prev,const CString &file )
{
	bool res = CEntityObject::Init( ie,prev,file );
	return res;
}

//////////////////////////////////////////////////////////////////////////
void CEnvironementProbeObject::InitVariables()
{
	CVarEnumList<int>* enumList = new CVarEnumList<int>;
	enumList->AddItem( "32",32 );
	enumList->AddItem( "64",64 );
	enumList->AddItem( "128",128 );
	enumList->AddItem( "256",256 );
	enumList->AddItem( "512",512 );
	enumList->AddItem( "1024",1024 );
	enumList->EnableSort(false);
	m_cubemap_resolution->SetEnumList(enumList);
	m_cubemap_resolution->SetDisplayValue("128");

	AddVariable(m_cubemap_resolution,CString("cubemap_resolution"));
	AddVariable(m_preview_cubemap,CString("preview_cubemap"),functor(*this,&CEnvironementProbeObject::OnPreviewCubemap),IVariable::DT_SIMPLE);
	m_preview_cubemap->Set(false);

	CEntityObject::InitVariables();
}

//////////////////////////////////////////////////////////////////////////
void CEnvironementProbeObject::Serialize( CObjectArchive &ar )
{
	m_preview_cubemap->Set(false);
	CEntityObject::Serialize(ar);
}

//////////////////////////////////////////////////////////////////////////
void CEnvironementProbeObject::BeginEditParams( IEditor *ie,int flags )
{
  IVariable * pPrivateVar = GetProperties()->FindVariable("_texture_deferred_cubemap",true);
  if ( !pPrivateVar )
    return;

  IVariable * pPublicVar = GetProperties()->FindVariable("texture_deferred_cubemap",true);
  if ( !pPublicVar )
    return;

  CString privateStr,publicStr;

  pPublicVar->Get(publicStr);
  pPrivateVar->Get(privateStr);

  if ( publicStr.IsEmpty() && !privateStr.IsEmpty() )
    pPublicVar->Set(privateStr);

	CEntityObject::BeginEditParams(ie,flags);
	if (!s_panelProbeID)
	{
		s_pProbePanel = new CEnvironmentProbePanel();
		s_pProbePanel->Create(CEnvironmentProbePanel::IDD, AfxGetMainWnd());
		s_panelProbeID = ie->AddRollUpPage(ROLLUP_OBJECTS, "Probe Functions", s_pProbePanel,true,3);
	}
	if (s_panelProbeID)
		s_pProbePanel->SetEntity(this);
}

//////////////////////////////////////////////////////////////////////////
void CEnvironementProbeObject::EndEditParams( IEditor *ie )
{
	if (s_pProbePanel)
	{
		ie->RemoveRollUpPage( ROLLUP_OBJECTS,s_panelProbeID );
		s_panelProbeID = 0;
		s_pProbePanel = 0;
	}
	CEntityObject::EndEditParams(ie);
}

//////////////////////////////////////////////////////////////////////////
void CEnvironementProbeObject::Display( DisplayContext &dc )
{
	Matrix34 wtm = GetWorldTM();
	Vec3 wp = wtm.GetTranslation();
	float fScale = GetHelperScale();

	if (IsHighlighted() && !IsFrozen())
	{
		dc.SetLineWidth(3);
	}

	if (IsSelected())
	{
		dc.SetSelectedColor();
	}
	else if (IsFrozen())
	{
		dc.SetFreezeColor();
	}
	else
	{
		dc.SetColor( GetColor() );
	}
	
	if ( !m_visualObject )
	{
		dc.PushMatrix( wtm );
		Vec3 sz(fScale*0.5f,fScale*0.5f,fScale*0.5f);
		dc.DrawWireBox( -sz,sz );

		// Draw radiuses if present and object selected.
		if (gSettings.viewports.bAlwaysShowRadiuses || IsSelected())
		{
			if( m_bBoxProjectedCM )
			{			
				// Draw helper for box projection.
				float fBoxWidth = m_fBoxWidth*0.5f;
				float fBoxHeight = m_fBoxHeight*0.5f;
				float fBoxLength = m_fBoxLength*0.5f;

				dc.SetColor( 0, 1, 0, 0.8f );
				dc.DrawWireBox(Vec3(-fBoxLength, -fBoxWidth, -fBoxHeight), Vec3(fBoxLength, fBoxWidth, fBoxHeight));
			}

			const Vec3& scale = GetScale();
			if (m_proximityRadius > 0 && (scale.x != 0.0f && scale.y != 0.0f && scale.z != 0.0f) )
			{
				dc.SetColor( 1,1,0,0.8f );
				if( m_bDisplayAbsoluteRadius )
					dc.DrawWireSphere( Vec3(0,0,0),Vec3( m_proximityRadius / scale.x,m_proximityRadius / scale.y,m_proximityRadius / scale.z) );
				else
					dc.DrawWireSphere( Vec3(0,0,0),m_proximityRadius );
			}
		}
		dc.PopMatrix();
	}

	if (IsHighlighted() && !IsFrozen())
	{
		dc.SetLineWidth(0);
	}

	if ( !m_visualObject )
	{
		DrawDefault( dc );
	}

	if (m_visualObject)
	{
		Matrix34 tm(wtm);
		float sz = m_helperScale*gSettings.gizmo.helpersScale;
		tm.ScaleColumn( Vec3(sz,sz,sz) );

		SRendParams rp;
    rp.AmbientColor = ColorF(0.0f, 0.0f, 0.0f, 1);
    rp.dwFObjFlags |= FOB_TRANS_MASK;
		rp.fAlpha = 1;
		rp.nDLightMask = 1;
		rp.pMatrix = &tm;
		rp.pMaterial = m_visualObject->GetMaterial();
		m_visualObject->Render( rp );
	}
}

//////////////////////////////////////////////////////////////////////////
//! Get bounding box of object in world coordinate space.
void CEnvironementProbeObject::GetBoundBox( AABB &box )
{
	const float fScale = GetHelperScale();
	const Vec3& position = GetPos();
	const Vec3 sz(fScale*0.5f,fScale*0.5f,fScale*0.5f);
	box.max = position + sz;
	box.min = position - sz;
}

void CEnvironementProbeObject::GetLocalBounds(AABB &aabb)
{
	const float fScale = GetHelperScale();
	const Vec3 sz(fScale*0.5f,fScale*0.5f,fScale*0.5f);
	aabb.max = sz;
	aabb.min = -sz;
}

//////////////////////////////////////////////////////////////////////////
void CEnvironementProbeObject::GenerateCubemap()
{
	CString levelfolder = CString(GetIEditor()->GetGameEngine()->GetLevelPath());
	CString levelname = Path::GetFile(levelfolder).MakeLower();
	CString fullGameFolder =  Path::GetGameFolder() + CString("\\");
	CString texturename = (GetName() + CString("_cm.tif")).MakeLower();
	CString relFolder = CString("Textures\\cubemaps\\") + levelname;
	CString relFilename = relFolder + "\\" + texturename;
	CString fullFolder = fullGameFolder + relFolder + "\\"; 
	CString fullFilename = fullGameFolder + relFilename;
	

	CFileUtil::CreateDirectory(fullFolder);

	int cubemapres = 256;
	m_cubemap_resolution->Get(cubemapres);
	if ( CubemapUtils::GenCubemapWithObjectPathAndSize(fullFilename,this,(int)cubemapres,false) )
	{
		IVariable * pVar = GetProperties()->FindVariable("texture_deferred_cubemap",true);
		if ( !pVar )
			return;
	
		Path::ConvertBackSlashToSlash(relFilename);
		pVar->Set(relFilename);
	
		if ( m_visualObject )
		{
			m_visualObject->SetMaterial(CreateMaterial());
		}
		UpdatePropertyPanel();
		UpdateLinks();
	}
}

//////////////////////////////////////////////////////////////////////////
IMaterial * CEnvironementProbeObject::CreateMaterial()
{
	CString deferredCubemapPath;
	CString matName;
  if (!GetProperties())
    return NULL;

	IVariable * pVar = GetProperties()->FindVariable("texture_deferred_cubemap",true);
	if ( !pVar )
		return NULL;

	pVar->Get(deferredCubemapPath);

	matName = Path::GetFileName(deferredCubemapPath);
	IMaterialManager* pMatMan = GetIEditor()->Get3DEngine()->GetMatMan();
	IMaterial* pMatSrc = pMatMan->LoadMaterial( "Editor/Objects/envcube", false,true );
	if( pMatSrc )
	{
		IMaterial * pMatDst = pMatMan->CreateMaterial( (const char*)matName, pMatSrc->GetFlags() | MTL_FLAG_NON_REMOVABLE );
		if( pMatDst )
		{
			SShaderItem& si = pMatSrc->GetShaderItem();
			SInputShaderResources isr = si.m_pShaderResources;
			isr.m_Textures[ EFTT_ENV ].m_Name = deferredCubemapPath;
			SShaderItem siDst = GetIEditor()->GetRenderer()->EF_LoadShaderItem( si.m_pShader->GetName(), true, 0, &isr, si.m_pShader->GetGenerationMask() );
			pMatDst->AssignShaderItem( siDst );
			return pMatDst;
		}
	}	
	return NULL;
}

//////////////////////////////////////////////////////////////////////////
void CEnvironementProbeObject::OnPreviewCubemap(IVariable*	piVariable)
{
	bool preview = false;
	piVariable->Get(preview);
	if ( preview )
	{
		CString base("Editor/Temp/");
		CFileUtil::CreateDirectory(base);
		CString entityname(GetName());
		CString newpath(base + entityname + ".cgf");
		CFileUtil::CopyFile("Editor/Objects/envcube.cgf",newpath);

		m_visualObject = GetIEditor()->Get3DEngine()->LoadStatObj(newpath, NULL, NULL, false );
		m_visualObject->SetMaterial(CreateMaterial());
		m_visualObject->AddRef();
	}
	else
	{
		if ( m_visualObject )
			m_visualObject->Release();
		m_visualObject = NULL;
	}
}

int CEnvironementProbeObject::AddEntityLink( const CString &name,GUID targetEntityId,const Quat& relRot,const Vec3& relPos )
{
  int ret = CEntityObject::AddEntityLink(name,targetEntityId,relRot,relPos);
  UpdateLinks();
  return ret;
}

void CEnvironementProbeObject::UpdateLinks()
{
  int count = GetEntityLinkCount();
  for ( int idx = 0; idx < count; idx ++ )
  {
    CEntityLink link = GetEntityLink(idx);
    CBaseObject * pObject = GetIEditor()->GetObjectManager()->FindObject(link.targetId);
    if ( !pObject )
      continue;

    CEntityObject * pTarget = NULL;
    if (pObject && pObject->IsKindOf(RUNTIME_CLASS(CEntityObject)))
      pTarget = (CEntityObject*)pObject;

    if ( !pTarget )
      continue;

    CVarBlock * pVarBlock = NULL;
    CString type = pObject->GetTypeDescription();
    if ( stricmp(type,"Light") == 0 )
    {
      pVarBlock = pTarget->GetProperties();
      if ( !pVarBlock )
        continue;
    }
    else if ( stricmp(type,"DestroyableLight") == 0 || stricmp(type,"RigidBodyLight") == 0 )
    {
      pVarBlock = pTarget->GetProperties2();
      if ( !pVarBlock )
        continue;
    }
    else
    {
      continue;
    }

    CVarBlock * pProperties = GetProperties();
    if ( !pProperties || !pVarBlock )
      continue;

    IVariable * pDstDeferredCubemap = pVarBlock->FindVariable("texture_deferred_cubemap",true);
    IVariable * pDeferredCubemap = pProperties->FindVariable("texture_deferred_cubemap",true);

    if ( !pDstDeferredCubemap || !pDeferredCubemap)
      continue;

    bool bDeferred = false;
    CString strCubemap;

    pDeferredCubemap->Get(strCubemap);
	pDstDeferredCubemap->Set(strCubemap);
  }
}

void CEnvironementProbeObject::OnPropertyChanged( IVariable * pVar )
{
  UpdateLinks(); 
  CBaseObject::OnPropertyChanged(pVar);
}

void CEnvironementProbeObject::OnMultiSelPropertyChanged( IVariable * pVar)
{
  UpdateLinks();
  CBaseObject::OnMultiSelPropertyChanged(pVar);
}
